﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Policy;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Xml;

namespace CPPEI.WebApi
{
    public static class ActionFactory
    {
        public static bool HasInitiated { get; private set; }

        private static List<ScenarioRegInfo> s_scenarioRegInfos = new List<ScenarioRegInfo>();

        public static void InitByConfig()
        {
            string configFilePath = HttpContext.Current.Server.MapPath("~/Configs/WebApi.config");
            HasInitiated = true;

            try
            {
                XmlDocument xDoc = new XmlDocument();
                xDoc.Load(configFilePath);

                XmlNodeList xWebApiList = xDoc.SelectNodes("./WebApi/Modules/Module");
                foreach (XmlNode xWebApi in xWebApiList)
                {
                    string assemblyFullName = xWebApi.Attributes["Assembly"].Value;
                    Assembly assembly = Assembly.Load(assemblyFullName);

                    Type[] types = assembly.GetTypes();
                    foreach (Type type in types)
                    {
                        ActionAttribute actionAttr = type.GetCustomAttribute<ActionAttribute>();

                        if (actionAttr != null)
                        {
                            ScenarioRegInfo scenario = GetScenario(actionAttr.Scenario);
                            if (scenario == null)
                            {
                                scenario = new ScenarioRegInfo(actionAttr.Scenario);
                                AddScenario(scenario);
                            }

                            if (scenario.Exist(actionAttr.Name))
                            {
                                throw new Exception(string.Format("{0}:{1} has exist!", actionAttr.Scenario, actionAttr.Name));
                            }

                            Type[] gTypes = type.BaseType.GetGenericArguments();

                            scenario.Add(new ActionRegInfo(actionAttr.Name, type, actionAttr.HttpMethod, actionAttr.IsAnonymous));
                        }
                    }
                }
            }
            catch(Exception ex)
            {

            }
        }

        public static void Init(string assemblyFolder)
        {
            string[] files = Directory.GetFiles(assemblyFolder, "*.dll");
            foreach (string file in files)
            {
                Assembly assembly = null;
                
                try
                {
                    assembly = Assembly.Load(File.ReadAllBytes(file));
                }
                catch(Exception ex)
                {
                    continue;
                }

                Type[] types = assembly.GetTypes();
                foreach (Type type in types)
                {
                    ActionAttribute actionAttr = type.GetCustomAttribute<ActionAttribute>();

                    if (actionAttr != null)
                    {
                        ScenarioRegInfo scenario = GetScenario(actionAttr.Scenario);
                        if (scenario == null)
                        {
                            scenario = new ScenarioRegInfo(actionAttr.Scenario);
                            AddScenario(scenario);
                        }

                        if (scenario.Exist(actionAttr.Name))
                        {
                            throw new Exception(string.Format("{0}:{1} has exist!", actionAttr.Scenario, actionAttr.Name));
                        }

                        Type[] gtypes = type.BaseType.GetGenericArguments();

                        scenario.Add(new ActionRegInfo(actionAttr.Name, type, actionAttr.HttpMethod, actionAttr.IsAnonymous));
                    }
                }
            }
        }

        public static void InitByAssemblies(Assembly[] assemblies)
        {
            foreach (var assembly in assemblies)
            {
                Type[] types = assembly.GetTypes();
                foreach (Type type in types)
                {
                    ActionAttribute actionAttr = type.GetCustomAttribute<ActionAttribute>();

                    if (actionAttr != null)
                    {
                        ScenarioRegInfo scenario = GetScenario(actionAttr.Scenario);
                        if (scenario == null)
                        {
                            scenario = new ScenarioRegInfo(actionAttr.Scenario);
                            AddScenario(scenario);
                        }

                        if (scenario.Exist(actionAttr.Name))
                        {
                            throw new Exception(string.Format("{0}:{1} has exist!", actionAttr.Scenario, actionAttr.Name));
                        }

                        //Type[] gtypes = type.BaseType.GetGenericArguments();

                        scenario.Add(new ActionRegInfo(actionAttr.Name, type, actionAttr.HttpMethod, actionAttr.IsAnonymous));
                    }
                }
            }
        }

        public static IAction GetAction(SysParams sysPrams)
        {
            ScenarioRegInfo scenario = GetScenario(sysPrams.Scenario);
            if (scenario == null)
            {
                return null;
            }

            ActionRegInfo actionRegInfo = scenario.Get(sysPrams.Action);
            if (actionRegInfo == null)
            {
                return null;
            }

            BaseAction action = (BaseAction)Activator.CreateInstance(actionRegInfo.Type);
            action.HttpMethod = actionRegInfo.HttpMethod;
            action.IsAnonymous = actionRegInfo.IsAnonymous;

            return action;
        }

        public static List<ScenarioRegInfo> GetScenarios()
        {
            return s_scenarioRegInfos;
        }

        public static ActionRegInfo GetAction(string scenario, string action)
        {
            ScenarioRegInfo scenarioRegInfo = GetScenario(scenario);
            if (scenarioRegInfo != null)
            {
                return scenarioRegInfo.Get(action);
            }
            return null;
        }

        public static ScenarioRegInfo GetScenario(string name)
        {
            for (int index = 0; index < s_scenarioRegInfos.Count; index++)
            {
                if(string.Equals(s_scenarioRegInfos[index].Name, name, StringComparison.CurrentCultureIgnoreCase))
                {
                    return s_scenarioRegInfos[index];
                }
            }
            return null;
        }

        public static Type GetActionParamsType(ActionRegInfo action)
        {
            return action.Type.BaseType.GetGenericArguments()[0];
        }

        public static Type GetActionResultType(ActionRegInfo action)
        {
            return action.Type.BaseType.GetGenericArguments()[1];
        }

        private static void AddScenario(ScenarioRegInfo scenario)
        {
            s_scenarioRegInfos.Add(scenario);
        }
    }

    public class ScenarioRegInfo
    {
        public string Name { get; set; }

        public List<ActionRegInfo> Actions { get; set; }

        public ScenarioRegInfo(string name)
        {
            Name = name;
            Actions = new List<ActionRegInfo>();
        }

        public bool Exist(string actionName)
        {
            for (int index = 0; index < Actions.Count; index++)
            {
                if (string.Equals(Actions[index].Name, actionName, StringComparison.CurrentCultureIgnoreCase))
                {
                    return true;
                }
            }
            return false;
        }

        public void Add(ActionRegInfo action)
        {
            Actions.Add(action);
        }

        public ActionRegInfo Get(string actionName)
        {
            for (int index = 0; index < Actions.Count; index++)
            {
                if (string.Equals(Actions[index].Name, actionName, StringComparison.CurrentCultureIgnoreCase))
                {
                    return Actions[index];
                }
            }
            return null;
        }
    }

    public class ActionRegInfo
    {
        public string Name { get; set; }
        public Type Type { get; set; }

        public string HttpMethod { get; set; }
        public bool IsAnonymous { get; set; }

        public ActionRegInfo(string name, Type type, string httpMethod, bool isAnonymous)
        {
            HttpMethod = httpMethod;
            IsAnonymous = isAnonymous;
            Name = name;
            Type = type;
        }
    }

    public class WebApiAssembly
    {
        public string Assembly { get; set; }

        public List<ActionEntry> Actions = new List<ActionEntry>();

        public WebApiAssembly(string assembly)
        {
            Assembly = assembly;
        }

        public class ActionEntry
        {
            public string Type { get; set; }

            public ActionEntry(string type)
            {
                Type = type;
            }
        }
    }
}
